﻿// ----------------------------------------------------------------------
// <copyright file="BuildReleaseNotesDocumentGenerator.cs" author="Gert Christiansen">
//     Copyright (c) Gert Christiansen.
// </copyright>
// ------------------------------------------------------------------------
namespace TfsBuildExtensions.Activities.TeamFoundationServer
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using DocumentFormat.OpenXml;
    using DocumentFormat.OpenXml.Packaging;
    using DocumentFormat.OpenXml.Wordprocessing;
    using Microsoft.TeamFoundation.Build.Client;
    using Microsoft.TeamFoundation.TestManagement.Client;
    using Microsoft.TeamFoundation.VersionControl.Client;
    using Microsoft.TeamFoundation.WorkItemTracking.Client;
    using WordDocumentGenerator.Library;
    using A = DocumentFormat.OpenXml.Drawing;
    using DW = DocumentFormat.OpenXml.Drawing.Wordprocessing;
    using PIC = DocumentFormat.OpenXml.Drawing.Pictures;

    /// <summary>
    /// Generator for Build Release Notes
    /// </summary>
    public class BuildReleaseNotesDocumentGenerator : DocumentGenerator
    {
        #region Content Control Tags
        const string PlaceholderBuildNumber = "PlaceholderBuildNumber";
        const string PlaceholderChangeset = "PlaceholderChangeset";
        const string PlaceholderBuildStatus = "PlaceholderBuildStatus";
        const string PlaceholderTestStatus = "PlaceholderTestStatus";
        const string PlaceholderStartTime = "PlaceholderStartTime";
        const string PlaceholderFinishTime = "PlaceholderFinishTime";
        const string PlaceholderDuration = "PlaceholderDuration";
        const string PlaceholderRequestedBy = "PlaceholderRequestedBy";
        const string PlaceholderUri = "PlaceholderUri";

        const string PlaceholderLatestBuildNumber = "PlaceholderLatestBuildNumber";
        const string PlaceholderLatestChangeset = "PlaceholderLatestChangeset";
        const string PlaceholderLatestBuildStatus = "PlaceholderLatestBuildStatus";
        const string PlaceholderLatestTestStatus = "PlaceholderLatestTestStatus";
        const string PlaceholderLatestStartTime = "PlaceholderLatestStartTime";
        const string PlaceholderLatestFinishTime = "PlaceholderLatestFinishTime";
        const string PlaceholderLatestDuration = "PlaceholderLatestDuration";
        const string PlaceholderLatestRequestedBy = "PlaceholderLatestRequestedBy";
        const string PlaceholderLatestUri = "PlaceholderLatestUri";

        const string PlaceholderConfigurationFullName = "PlaceholderConfigurationFullName";
        const string PlaceholderContainerProjects = "PlaceholderContainerProjects";
        const string PlaceholderProjectDetailRow = "PlaceholderProjectDetailRow";
        const string PlaceholderProjectServerPath = "PlaceholderProjectServerPath";
        const string PlaceholderProjectStartTime = "PlaceholderProjectStartTime";
        const string PlaceholderProjectFinishTime = "PlaceholderProjectFinishTime";
        const string PlaceholderProjectCompilationErrors = "PlaceholderProjectCompilationErrors";
        const string PlaceholderProjectCompilationWarnings = "PlaceholderProjectCompilationWarnings";
        const string PlaceholderProjectStaticAnalysisErrors = "PlaceholderProjectStaticAnalysisErrors";
        const string PlaceholderProjectStaticAnalysisWarnings = "PlaceholderProjectStaticAnalysisWarnings";

        const string PlaceholderAbstractDevelopers = "PlaceholderAbstractDevelopers";
        const string PlaceholderAbstractNumberOfWorkItems = "PlaceholderAbstractNumberOfWorkItems";
        const string PlaceholderAbstractWorkItemCreators = "PlaceholderAbstractWorkItemCreators";
        const string PlaceholderAbstractNumberOfChangesets = "PlaceholderAbstractNumberOfChangesets";
        const string PlaceholderAbstractTotalNumberOfChanges = "PlaceholderAbstractTotalNumberOfChanges";
        const string PlaceholderAbstractChangesetPeriodInDays = "PlaceholderAbstractChangesetPeriodInDays";
        const string PlaceholderAbstractBuildPeriodInDays = "PlaceholderAbstractBuildPeriodInDays";

        const string PlaceholderContainerConfigurations = "PlaceholderContainerConfigurations";
        const string PlaceholderConfigurationDetailRow = "PlaceholderConfigurationDetailRow";
        const string PlaceholderConfigurationCompilationErrors = "PlaceholderConfigurationCompilationErrors";
        const string PlaceholderConfigurationCompilationWarnings = "PlaceholderConfigurationCompilationWarnings";
        const string PlaceholderConfigurationStaticAnalysisErrors = "PlaceholderConfigurationStaticAnalysisErrors";
        const string PlaceholderConfigurationStaticAnalysisWarnings = "PlaceholderConfigurationStaticAnalysisWarnings";

        const string PlaceholderContainerChangesets = "PlaceholderContainerChangesets";
        const string PlaceholderChangesetDetailRow = "PlaceholderChangesetDetailRow";
        const string PlaceholderChangesetId = "PlaceholderChangesetId";
        const string PlaceholderChangesetComment = "PlaceholderChangesetComment";

        const string PlaceholderContainerWorkItems = "PlaceholderContainerWorkItems";
        const string PlaceholderWorkItemDetailRow = "PlaceholderWorkItemDetailRow";
        const string PlaceholderWorkItemId = "PlaceholderWorkItemId";
        const string PlaceholderWorkItemTitle = "PlaceholderWorkItemTitle";

        const string PlaceholderContainerTestCaseResults = "PlaceholderContainerTestCaseResults";
        const string PlaceholderTestCaseResultDetailRow = "PlaceholderTestCaseResultDetailRow";
        const string PlaceholderTestCaseResultArea = "PlaceholderTestCaseResultArea";
        const string PlaceholderTestCaseResultTitle = "PlaceholderTestCaseResultTitle";
        const string PlaceholderTestCaseResultOutcome = "PlaceholderTestCaseResultOutcome";
        #endregion

        #region Constructor

        /// <summary>
        /// Initializes a new instance of the <see cref="BuildReleaseNotesDocumentGenerator"/> class.
        /// </summary>
        /// <param name="generationInfo">The generation info.</param>
        public BuildReleaseNotesDocumentGenerator(DocumentGenerationInfo generationInfo)
            : base(generationInfo)
        {

        }

        #endregion

        #region Overridden methods

        /// <summary>
        /// Gets the place holder tag to type collection.
        /// </summary>
        /// <returns></returns>
        protected override Dictionary<string, PlaceHolderType> GetPlaceHolderTagToTypeCollection()
        {
            Dictionary<string, PlaceHolderType> placeHolderTagToTypeCollection = new Dictionary<string, PlaceHolderType>();

            // Handle non recursive placeholders
            placeHolderTagToTypeCollection.Add(PlaceholderBuildNumber, PlaceHolderType.NonRecursive);
            placeHolderTagToTypeCollection.Add(PlaceholderChangeset, PlaceHolderType.NonRecursive);
            placeHolderTagToTypeCollection.Add(PlaceholderBuildStatus, PlaceHolderType.NonRecursive);
            placeHolderTagToTypeCollection.Add(PlaceholderTestStatus, PlaceHolderType.NonRecursive);
            placeHolderTagToTypeCollection.Add(PlaceholderStartTime, PlaceHolderType.NonRecursive);
            placeHolderTagToTypeCollection.Add(PlaceholderFinishTime, PlaceHolderType.NonRecursive);
            placeHolderTagToTypeCollection.Add(PlaceholderDuration, PlaceHolderType.NonRecursive);
            placeHolderTagToTypeCollection.Add(PlaceholderRequestedBy, PlaceHolderType.NonRecursive);
            placeHolderTagToTypeCollection.Add(PlaceholderUri, PlaceHolderType.NonRecursive);

            placeHolderTagToTypeCollection.Add(PlaceholderLatestBuildNumber, PlaceHolderType.NonRecursive);
            placeHolderTagToTypeCollection.Add(PlaceholderLatestChangeset, PlaceHolderType.NonRecursive);
            placeHolderTagToTypeCollection.Add(PlaceholderLatestBuildStatus, PlaceHolderType.NonRecursive);
            placeHolderTagToTypeCollection.Add(PlaceholderLatestTestStatus, PlaceHolderType.NonRecursive);
            placeHolderTagToTypeCollection.Add(PlaceholderLatestStartTime, PlaceHolderType.NonRecursive);
            placeHolderTagToTypeCollection.Add(PlaceholderLatestFinishTime, PlaceHolderType.NonRecursive);
            placeHolderTagToTypeCollection.Add(PlaceholderLatestDuration, PlaceHolderType.NonRecursive);
            placeHolderTagToTypeCollection.Add(PlaceholderLatestRequestedBy, PlaceHolderType.NonRecursive);
            placeHolderTagToTypeCollection.Add(PlaceholderLatestUri, PlaceHolderType.NonRecursive);

            placeHolderTagToTypeCollection.Add(PlaceholderProjectServerPath, PlaceHolderType.NonRecursive);
            placeHolderTagToTypeCollection.Add(PlaceholderProjectStartTime, PlaceHolderType.NonRecursive);
            placeHolderTagToTypeCollection.Add(PlaceholderProjectFinishTime, PlaceHolderType.NonRecursive);
            placeHolderTagToTypeCollection.Add(PlaceholderProjectCompilationErrors, PlaceHolderType.NonRecursive);
            placeHolderTagToTypeCollection.Add(PlaceholderProjectCompilationWarnings, PlaceHolderType.NonRecursive);
            placeHolderTagToTypeCollection.Add(PlaceholderProjectStaticAnalysisErrors, PlaceHolderType.NonRecursive);
            placeHolderTagToTypeCollection.Add(PlaceholderProjectStaticAnalysisWarnings, PlaceHolderType.NonRecursive);
            placeHolderTagToTypeCollection.Add(PlaceholderConfigurationFullName, PlaceHolderType.NonRecursive);
            placeHolderTagToTypeCollection.Add(PlaceholderConfigurationCompilationErrors, PlaceHolderType.NonRecursive);
            placeHolderTagToTypeCollection.Add(PlaceholderConfigurationCompilationWarnings, PlaceHolderType.NonRecursive);
            placeHolderTagToTypeCollection.Add(PlaceholderConfigurationStaticAnalysisErrors, PlaceHolderType.NonRecursive);
            placeHolderTagToTypeCollection.Add(PlaceholderConfigurationStaticAnalysisWarnings, PlaceHolderType.NonRecursive);

            placeHolderTagToTypeCollection.Add(PlaceholderChangesetId, PlaceHolderType.NonRecursive);
            placeHolderTagToTypeCollection.Add(PlaceholderChangesetComment, PlaceHolderType.NonRecursive);
            placeHolderTagToTypeCollection.Add(PlaceholderWorkItemId, PlaceHolderType.NonRecursive);
            placeHolderTagToTypeCollection.Add(PlaceholderWorkItemTitle, PlaceHolderType.NonRecursive);

            placeHolderTagToTypeCollection.Add(PlaceholderTestCaseResultArea, PlaceHolderType.NonRecursive);
            placeHolderTagToTypeCollection.Add(PlaceholderTestCaseResultTitle, PlaceHolderType.NonRecursive);
            placeHolderTagToTypeCollection.Add(PlaceholderTestCaseResultOutcome, PlaceHolderType.NonRecursive);

            placeHolderTagToTypeCollection.Add(PlaceholderAbstractDevelopers, PlaceHolderType.NonRecursive);
            placeHolderTagToTypeCollection.Add(PlaceholderAbstractNumberOfWorkItems, PlaceHolderType.NonRecursive);
            placeHolderTagToTypeCollection.Add(PlaceholderAbstractWorkItemCreators, PlaceHolderType.NonRecursive);
            placeHolderTagToTypeCollection.Add(PlaceholderAbstractNumberOfChangesets, PlaceHolderType.NonRecursive);
            placeHolderTagToTypeCollection.Add(PlaceholderAbstractTotalNumberOfChanges, PlaceHolderType.NonRecursive);
            placeHolderTagToTypeCollection.Add(PlaceholderAbstractChangesetPeriodInDays, PlaceHolderType.NonRecursive);
            placeHolderTagToTypeCollection.Add(PlaceholderAbstractBuildPeriodInDays, PlaceHolderType.NonRecursive);

            // Handle container placeholders            
            placeHolderTagToTypeCollection.Add(PlaceholderContainerConfigurations, PlaceHolderType.Container);
            placeHolderTagToTypeCollection.Add(PlaceholderContainerProjects, PlaceHolderType.Container);
            placeHolderTagToTypeCollection.Add(PlaceholderContainerChangesets, PlaceHolderType.Container);
            placeHolderTagToTypeCollection.Add(PlaceholderContainerWorkItems, PlaceHolderType.Container);
            placeHolderTagToTypeCollection.Add(PlaceholderContainerTestCaseResults, PlaceHolderType.Container);

            // Handle recursive placeholders            
            placeHolderTagToTypeCollection.Add(PlaceholderConfigurationDetailRow, PlaceHolderType.Recursive);
            placeHolderTagToTypeCollection.Add(PlaceholderProjectDetailRow, PlaceHolderType.Recursive);
            placeHolderTagToTypeCollection.Add(PlaceholderChangesetDetailRow, PlaceHolderType.Recursive);
            placeHolderTagToTypeCollection.Add(PlaceholderWorkItemDetailRow, PlaceHolderType.Recursive);
            placeHolderTagToTypeCollection.Add(PlaceholderTestCaseResultDetailRow, PlaceHolderType.Recursive);

            return placeHolderTagToTypeCollection;
        }

        /// <summary>
        /// Ignore placeholder found.
        /// </summary>
        /// <param name="placeholderTag">The placeholder tag.</param>
        /// <param name="openXmlElementDataContext">The open XML element data context.</param>
        protected override void IgnorePlaceholderFound(string placeholderTag, OpenXmlElementDataContext openXmlElementDataContext)
        {
        }

        /// <summary>
        /// Non recursive placeholder found.
        /// </summary>
        /// <param name="placeholderTag">The placeholder tag.</param>
        /// <param name="openXmlElementDataContext">The open XML element data context.</param>
        protected override void NonRecursivePlaceholderFound(string placeholderTag, OpenXmlElementDataContext openXmlElementDataContext)
        {
            if (openXmlElementDataContext == null || openXmlElementDataContext.Element == null || openXmlElementDataContext.DataContext == null)
            {
                return;
            }

            string tagPlaceHolderValue = string.Empty;
            string tagGuidPart = string.Empty;
            GetTagValue(openXmlElementDataContext.Element as SdtElement, out tagPlaceHolderValue, out tagGuidPart);

            string tagValue = string.Empty;
            string content = string.Empty;

            IBuildDetail buildDetail = null;
            IBuildDetail latestBuildDetail = null;
            IConfigurationSummary summary = null;
            IBuildProjectNode node = null;
            ITestCaseResult testCaseResult = null;
            Changeset changeset = null;
            WorkItem workItem = null;

            var context = (openXmlElementDataContext.DataContext) as BuildDataContext;
            if (context != null)
            {
                buildDetail = context.BuildDetail;
                latestBuildDetail = context.LatestBuildDetail;
            }
            else
            {
                summary = (openXmlElementDataContext.DataContext) as IConfigurationSummary;
                node = (openXmlElementDataContext.DataContext) as IBuildProjectNode;
                testCaseResult = (openXmlElementDataContext.DataContext) as ITestCaseResult;
                changeset = (openXmlElementDataContext.DataContext) as Changeset;
                workItem = (openXmlElementDataContext.DataContext) as WorkItem;
            }

            switch (tagPlaceHolderValue)
            {
                case PlaceholderBuildNumber:
                    tagValue = buildDetail.Uri.ToString();
                    content = buildDetail.BuildNumber;
                    break;
                case PlaceholderChangeset:
                    tagValue = buildDetail.Uri.ToString();
                    content = buildDetail.SourceGetVersion;
                    break;
                case PlaceholderBuildStatus:
                    tagValue = buildDetail.Uri.ToString();
                    content = buildDetail.CompilationStatus.ToString();
                    break;
                case PlaceholderTestStatus:
                    tagValue = buildDetail.Uri.ToString();
                    content = buildDetail.TestStatus.ToString();
                    break;
                case PlaceholderStartTime:
                    tagValue = buildDetail.Uri.ToString();
                    content = buildDetail.StartTime.ToString("U");
                    break;
                case PlaceholderFinishTime:
                    tagValue = buildDetail.Uri.ToString();
                    content = buildDetail.FinishTime.ToString("U");
                    break;
                case PlaceholderDuration:
                    tagValue = buildDetail.Uri.ToString();
                    // This build is still InProgress so there's no FinishTime yet
                    var duration = DateTime.Now - buildDetail.StartTime;
                    content = ((int)(duration.TotalSeconds)).ToString();
                    break;
                case PlaceholderRequestedBy:
                    tagValue = buildDetail.Uri.ToString();
                    content = buildDetail.RequestedBy;
                    break;
                case PlaceholderUri:
                    tagValue = buildDetail.Uri.ToString();
                    content = buildDetail.Uri.ToString();
                    break;
                case PlaceholderLatestBuildNumber:
                    tagValue = latestBuildDetail.Uri.ToString();
                    content = latestBuildDetail.BuildNumber;
                    break;
                case PlaceholderLatestChangeset:
                    tagValue = latestBuildDetail.Uri.ToString();
                    content = latestBuildDetail.SourceGetVersion;
                    break;
                case PlaceholderLatestBuildStatus:
                    tagValue = latestBuildDetail.Uri.ToString();
                    content = latestBuildDetail.CompilationStatus.ToString();
                    break;
                case PlaceholderLatestTestStatus:
                    tagValue = latestBuildDetail.Uri.ToString();
                    content = latestBuildDetail.TestStatus.ToString();
                    break;
                case PlaceholderLatestStartTime:
                    tagValue = latestBuildDetail.Uri.ToString();
                    content = latestBuildDetail.StartTime.ToString("U");
                    break;
                case PlaceholderLatestFinishTime:
                    tagValue = latestBuildDetail.Uri.ToString();
                    content = latestBuildDetail.FinishTime.ToString("U");
                    break;
                case PlaceholderLatestDuration:
                    tagValue = latestBuildDetail.Uri.ToString();
                    var latestDuration = latestBuildDetail.FinishTime - latestBuildDetail.StartTime;
                    content = ((int)(latestDuration.TotalSeconds)).ToString();
                    break;
                case PlaceholderLatestRequestedBy:
                    tagValue = latestBuildDetail.Uri.ToString();
                    content = latestBuildDetail.RequestedBy;
                    break;
                case PlaceholderLatestUri:
                    tagValue = latestBuildDetail.Uri.ToString();
                    content = latestBuildDetail.Uri.ToString();
                    break;
                case PlaceholderProjectServerPath:
                    tagValue = node.Node.Id.ToString();
                    content = node.ServerPath;
                    break;
                case PlaceholderProjectCompilationErrors:
                    tagValue = node.Node.Id.ToString();
                    content = node.CompilationErrors.ToString();
                    break;
                case PlaceholderProjectCompilationWarnings:
                    tagValue = node.Node.Id.ToString();
                    content = node.CompilationWarnings.ToString();
                    break;
                case PlaceholderProjectStaticAnalysisErrors:
                    tagValue = node.Node.Id.ToString();
                    content = node.StaticAnalysisErrors.ToString();
                    break;
                case PlaceholderProjectStaticAnalysisWarnings:
                    tagValue = node.Node.Id.ToString();
                    content = node.StaticAnalysisWarnings.ToString();
                    break;
                case PlaceholderProjectStartTime:
                    tagValue = node.Node.Id.ToString();
                    content = node.StartTime.ToString("U");
                    break;
                case PlaceholderProjectFinishTime:
                    tagValue = node.Node.Id.ToString();
                    content = node.FinishTime.ToString("U");
                    break;
                case PlaceholderConfigurationFullName:
                    tagValue = summary.Id.ToString();
                    content = GetSummaryFullName(summary);
                    break;
                case PlaceholderConfigurationCompilationErrors:
                    tagValue = summary.Id.ToString();
                    content = summary.TotalCompilationErrors.ToString();
                    break;
                case PlaceholderConfigurationCompilationWarnings:
                    tagValue = summary.Id.ToString();
                    content = summary.TotalCompilationErrors.ToString();
                    break;
                case PlaceholderConfigurationStaticAnalysisErrors:
                    tagValue = summary.Id.ToString();
                    content = summary.TotalCompilationErrors.ToString();
                    break;
                case PlaceholderConfigurationStaticAnalysisWarnings:
                    tagValue = summary.Id.ToString();
                    content = summary.TotalCompilationErrors.ToString();
                    break;
                case PlaceholderChangesetId:
                    tagValue = changeset.ChangesetId.ToString();
                    content = changeset.ChangesetId.ToString();
                    break;
                case PlaceholderChangesetComment:
                    tagValue = changeset.ChangesetId.ToString();
                    content = changeset.Comment;
                    break;
                case PlaceholderWorkItemId:
                    tagValue = workItem.Id.ToString();
                    content = workItem.Id.ToString();
                    break;
                case PlaceholderWorkItemTitle:
                    tagValue = workItem.Id.ToString();
                    content = workItem.Title;
                    break;
                case PlaceholderTestCaseResultArea:
                    tagValue = testCaseResult.DateCompleted.ToFileTime().ToString();
                    content = testCaseResult.TestCaseArea;
                    break;
                case PlaceholderTestCaseResultTitle:
                    tagValue = testCaseResult.DateCompleted.ToFileTime().ToString();
                    content = testCaseResult.TestCaseTitle;
                    break;
                case PlaceholderTestCaseResultOutcome:
                    tagValue = testCaseResult.DateCompleted.ToFileTime().ToString();
                    content = testCaseResult.Outcome.ToString();
                    AddOutcomeGraphic(openXmlElementDataContext.Element as SdtElement, testCaseResult.Outcome);
                    break;
                case PlaceholderAbstractChangesetPeriodInDays:
                    tagValue = context.Abstract.ChangesetPeriodInDays.ToString();
                    content = context.Abstract.ChangesetPeriodInDays.ToString();
                    break;
                case PlaceholderAbstractDevelopers:
                    tagValue = context.Abstract.Developers;
                    content = context.Abstract.Developers;
                    break;
                case PlaceholderAbstractNumberOfWorkItems:
                    tagValue = context.Abstract.NumberOfWorkItems.ToString();
                    content = context.Abstract.NumberOfWorkItems.ToString();
                    break;
                case PlaceholderAbstractWorkItemCreators:
                    tagValue = context.Abstract.WorkItemCreators;
                    content = context.Abstract.WorkItemCreators;
                    break;
                case PlaceholderAbstractNumberOfChangesets:
                    tagValue = context.Abstract.NumberOfChangesets.ToString();
                    content = context.Abstract.NumberOfChangesets.ToString();
                    break;
                case PlaceholderAbstractTotalNumberOfChanges:
                    tagValue = context.Abstract.TotalNumberOfChanges.ToString();
                    content = context.Abstract.TotalNumberOfChanges.ToString();
                    break;
                case PlaceholderAbstractBuildPeriodInDays:
                    tagValue = context.Abstract.BuildPeriodInDays.ToString();
                    content = context.Abstract.BuildPeriodInDays.ToString();
                    break;
            }

            // Set the tag for the content control
            if (!string.IsNullOrEmpty(tagValue))
            {
                this.SetTagValue(openXmlElementDataContext.Element as SdtElement, GetFullTagValue(tagPlaceHolderValue, tagValue));
            }

            // Set text without data binding
            this.SetContentOfContentControl(openXmlElementDataContext.Element as SdtElement, content);
        }

        static void AddImageBeforeElement(SdtElement sdtElement, ButtonType buttonType)
        {
            var mainPart = sdtElement.Parent.Ancestors<Document>().First().MainDocumentPart;
            ImagePart imagePart = mainPart.AddImagePart(ImagePartType.Png);
            OutcomeButtons.FeedContentToImagePart(imagePart, buttonType);
            var relationshipId = mainPart.GetIdOfPart(imagePart);

            // Define the reference of the image.
            var element =
                 new Drawing(
                     new DW.Inline(
                         new DW.Extent() { Cx = 130000L, Cy = 130000L },
                         new DW.EffectExtent()
                         {
                             LeftEdge = 0L,
                             TopEdge = 0L,
                             RightEdge = 0L,
                             BottomEdge = 0L
                         },
                         new DW.DocProperties()
                         {
                             Id = (UInt32Value)1U,
                             Name = "Picture 1"
                         },
                         new DW.NonVisualGraphicFrameDrawingProperties(
                             new A.GraphicFrameLocks() { NoChangeAspect = true }),
                         new A.Graphic(
                             new A.GraphicData(
                                 new PIC.Picture(
                                     new PIC.NonVisualPictureProperties(
                                         new PIC.NonVisualDrawingProperties()
                                         {
                                             Id = (UInt32Value)0U,
                                             Name = buttonType.ToString()
                                         },
                                         new PIC.NonVisualPictureDrawingProperties()),
                                     new PIC.BlipFill(
                                         new A.Blip(
                                             new A.BlipExtensionList(
                                                 new A.BlipExtension()
                                                 {
                                                     Uri =
                                                       "{28A0092B-C50C-407E-A947-70E740481C1C}"
                                                 })
                                         )
                                         {
                                             Embed = relationshipId,
                                             CompressionState =
                                             A.BlipCompressionValues.Print
                                         },
                                         new A.Stretch(
                                             new A.FillRectangle())),
                                     new PIC.ShapeProperties(
                                         new A.Transform2D(
                                             new A.Offset() { X = -1000000L, Y = -1000000L },
                                             new A.Extents() { Cx = 5000000L, Cy = 5000000L }),
                                         new A.PresetGeometry(
                                             new A.AdjustValueList()
                                         ) { Preset = A.ShapeTypeValues.Rectangle }))
                             ) { Uri = "http://schemas.openxmlformats.org/drawingml/2006/picture" })
                     )
                     {
                         DistanceFromTop = (UInt32Value)0U,
                         DistanceFromBottom = (UInt32Value)0U,
                         DistanceFromLeft = (UInt32Value)0U,
                         DistanceFromRight = (UInt32Value)0U
                     });

            sdtElement.Parent.Parent.PreviousSibling().InsertAt(new Paragraph((new Run(element))), 0);
        }

        static void AddOutcomeGraphic(SdtElement sdtElement, TestOutcome testOutcome)
        {
            switch (testOutcome)
            {
                case TestOutcome.Aborted:
                    AddImageBeforeElement(sdtElement, ButtonType.Red);
                    break;
                case TestOutcome.Blocked:
                    AddImageBeforeElement(sdtElement, ButtonType.Red);
                    break;
                case TestOutcome.Error:
                    AddImageBeforeElement(sdtElement, ButtonType.Red);
                    break;
                case TestOutcome.Failed:
                    AddImageBeforeElement(sdtElement, ButtonType.Red);
                    break;
                case TestOutcome.Inconclusive:
                    AddImageBeforeElement(sdtElement, ButtonType.Yellow);
                    break;
                case TestOutcome.None:
                    AddImageBeforeElement(sdtElement, ButtonType.Yellow);
                    break;
                case TestOutcome.NotExecuted:
                    AddImageBeforeElement(sdtElement, ButtonType.Yellow);
                    break;
                case TestOutcome.Passed:
                    AddImageBeforeElement(sdtElement, ButtonType.Green);
                    break;
                case TestOutcome.Timeout:
                    AddImageBeforeElement(sdtElement, ButtonType.Red);
                    break;
                case TestOutcome.Unspecified:
                    AddImageBeforeElement(sdtElement, ButtonType.Yellow);
                    break;
                case TestOutcome.Warning:
                    AddImageBeforeElement(sdtElement, ButtonType.Yellow);
                    break;
                default:
                    break;
            }
        }

        static string GetSummaryFullName(IConfigurationSummary summary)
        {
            return summary.FullName;
        }

        /// <summary>
        /// Recursive placeholder found.
        /// </summary>
        /// <param name="placeholderTag">The placeholder tag.</param>
        /// <param name="openXmlElementDataContext">The open XML element data context.</param>
        protected override void RecursivePlaceholderFound(string placeholderTag, OpenXmlElementDataContext openXmlElementDataContext)
        {
            if (openXmlElementDataContext == null || openXmlElementDataContext.Element == null || openXmlElementDataContext.DataContext == null)
            {
                return;
            }

            string tagPlaceHolderValue = string.Empty;
            string tagGuidPart = string.Empty;
            GetTagValue(openXmlElementDataContext.Element as SdtElement, out tagPlaceHolderValue, out tagGuidPart);

            switch (tagPlaceHolderValue)
            {
                case PlaceholderConfigurationDetailRow:
                    foreach (var summary in ((openXmlElementDataContext.DataContext) as BuildDataContext).ConfigurationSummaries)
                    {
                        SdtElement clonedElement = this.CloneElementAndSetContentInPlaceholders(new OpenXmlElementDataContext() { Element = openXmlElementDataContext.Element, DataContext = summary });
                    }
                    openXmlElementDataContext.Element.Remove();
                    break;
                case PlaceholderProjectDetailRow:
                    foreach (var node in ((openXmlElementDataContext.DataContext) as BuildDataContext).BuildProjectNodes)
                    {
                        SdtElement clonedElement = this.CloneElementAndSetContentInPlaceholders(new OpenXmlElementDataContext() { Element = openXmlElementDataContext.Element, DataContext = node });
                    }
                    openXmlElementDataContext.Element.Remove();
                    break;
                case PlaceholderChangesetDetailRow:
                    foreach (var changeset in ((openXmlElementDataContext.DataContext) as BuildDataContext).Changesets)
                    {
                        SdtElement clonedElement = this.CloneElementAndSetContentInPlaceholders(new OpenXmlElementDataContext() { Element = openXmlElementDataContext.Element, DataContext = changeset });
                    }
                    openXmlElementDataContext.Element.Remove();
                    break;
                case PlaceholderWorkItemDetailRow:
                    foreach (var workItem in ((openXmlElementDataContext.DataContext) as BuildDataContext).WorkItems)
                    {
                        SdtElement clonedElement = this.CloneElementAndSetContentInPlaceholders(new OpenXmlElementDataContext() { Element = openXmlElementDataContext.Element, DataContext = workItem });
                    }
                    openXmlElementDataContext.Element.Remove();
                    break;
                case PlaceholderTestCaseResultDetailRow:
                    foreach (var testCaseResult in ((openXmlElementDataContext.DataContext) as BuildDataContext).TestCaseResults)
                    {
                        SdtElement clonedElement = this.CloneElementAndSetContentInPlaceholders(new OpenXmlElementDataContext() { Element = openXmlElementDataContext.Element, DataContext = testCaseResult });
                    }
                    openXmlElementDataContext.Element.Remove();
                    break;
            }
        }

        /// <summary>
        /// Container placeholder found.
        /// </summary>
        /// <param name="placeholderTag">The placeholder tag.</param>
        /// <param name="openXmlElementDataContext">The open XML element data context.</param>
        protected override void ContainerPlaceholderFound(string placeholderTag, OpenXmlElementDataContext openXmlElementDataContext)
        {
            if (openXmlElementDataContext == null || openXmlElementDataContext.Element == null || openXmlElementDataContext.DataContext == null)
            {
                return;
            }

            string tagPlaceHolderValue = string.Empty;
            string tagGuidPart = string.Empty;
            GetTagValue(openXmlElementDataContext.Element as SdtElement, out tagPlaceHolderValue, out tagGuidPart);

            string tagValue = string.Empty;
            string content = string.Empty;

            switch (tagPlaceHolderValue)
            {
                case PlaceholderContainerConfigurations:
                    tagValue = SetPlaceholderTagAndContent(openXmlElementDataContext, tagPlaceHolderValue, tagValue);
                    break;
                case PlaceholderContainerProjects:
                    tagValue = SetPlaceholderTagAndContent(openXmlElementDataContext, tagPlaceHolderValue, tagValue);
                    break;
                case PlaceholderContainerChangesets:
                    tagValue = SetPlaceholderTagAndContent(openXmlElementDataContext, tagPlaceHolderValue, tagValue);
                    break;
                case PlaceholderContainerWorkItems:
                    tagValue = SetPlaceholderTagAndContent(openXmlElementDataContext, tagPlaceHolderValue, tagValue);
                    break;
                case PlaceholderContainerTestCaseResults:
                    tagValue = SetPlaceholderTagAndContent(openXmlElementDataContext, tagPlaceHolderValue, tagValue);
                    break;
            }
        }

        string SetPlaceholderTagAndContent(OpenXmlElementDataContext openXmlElementDataContext, string tagPlaceHolderValue, string tagValue)
        {
            tagValue = (openXmlElementDataContext.DataContext as BuildDataContext).BuildDetail.Uri.ToString();

            if (!string.IsNullOrEmpty(tagValue))
            {
                this.SetTagValue(openXmlElementDataContext.Element as SdtElement, GetFullTagValue(tagPlaceHolderValue, tagValue));
            }

            foreach (var v in openXmlElementDataContext.Element.Elements())
            {
                this.SetContentInPlaceholders(new OpenXmlElementDataContext() { Element = v, DataContext = openXmlElementDataContext.DataContext });
            }
            return tagValue;
        }
        #endregion
    }
}
